# Copyright (C) 2007 Laszlo Pandy <laszlok2@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

import gobject

import Utils
import Person
import Category
import BillsList
import Bill

class DuplicateNameException(Exception):
	pass

class UnknownNameException(Exception):
	pass

class Account(gobject.GObject):
	
	__gsignals__ = {
		"manager" : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
		"people" : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
		"categories" : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
		"bills" : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
		"loaded" : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () )
	}
	
	def __init__(self):
		gobject.GObject.__init__(self)
		
		#the person managing this account
		self.manager = None
		#all the known people including the manager
		self.people = set()
		#all the categories
		self.categories = set()
		#all the bills
		self.bills = BillsList.BillsList()
		#a lookup for the bills based on ID
		self.billsDict = {}
		self.__current_bill_ID = 0
		
	def __cmp__(self, object):
		if isinstance(object, Account):
			man = cmp(self.manager, object.manager)
			peo = (self.people == object.people)
			cate = (self.categories == object.categories)
			bill = (self.bills == object.bills)
			
			if man == 0 and peo and cate and bill:
				return 0
			else:
				return -1
		else:
			return -1
		
	def copy(self):
		a = Account()
			
		a.people = self.people.copy()
		a.setManager(self.manager)
		
		for cate in self.categories:
			a.categories.add(cate.copy())
		
		a.bills = self.bills.deep_copy()
		for bill in a.bills:
			cate = bill.getCategory()
			if cate:
				bill.setSharedBy(a.findCategory(cate.getName()))
			a.billsDict[bill.ID] = bill
		return a
	
	#############################
	#####  Person Functions  ####
	
	def addPerson(self, nameOrPerson):
		if nameOrPerson in self.people:
			raise DuplicateNameException(nameOrPerson)
		
		if type(nameOrPerson) == str:
			person = Person.Person(nameOrPerson)
		else:
			person = nameOrPerson
		self.people.add(person)
			
		if not self.manager:
			self.manager = person
			
		self.emit("people")
		return person
	
	def renamePerson(self, person, newName):
		if self.personExists(newName):
			raise DuplicateNameException(newName)
		
		person = self.findPerson(person)
		newPerson = Person.Person(newName)
		self._swapPerson(person, newPerson)
		
		self.emit("people")
	
	def removePerson(self, name):
		if self.personExists(name):
			person = self.findPerson(name)
			self._swapPerson(person, None)
			self.emit("people")
			
	def _swapPerson(self, person, newPerson):
		self.people.remove(person)
		if newPerson:
			self.people.add(newPerson)
	
		if self.manager == person:
			if newPerson:
				self.manager = newPerson
			elif len(self.people):
				self.manager = iter(self.people).next()
			else:
				self.manager = None
				
		[bill.swapPerson(person, newPerson) for bill in self.bills]
		[cate.swapPerson(person, newPerson) for cate in self.categories]
		
	def getPeople(self):
		return sorted(self.people)
		
	def findPerson(self, name):
		for person in self.people:
			if person == name or Utils.strEq(person.name, name):
				return person
		raise UnknownNameException(name)
	
	def personExists(self, name):
		for person in self.people:
			if person == name or Utils.strEq(person.name, name):
				return True
		return False	
		
	def getManager(self):
		return self.manager
		
	def isManager(self, person):
		return person == self.manager
		
	def setManager(self, person):
		self.people.add(person)
		self.manager = person
		self.emit("manager")
	
	#############################
	###  Category Functions  ####
	
	def addCategory(self, name, paidBy=None, sharedBy=None):
		if self.categoryExists(name):
			raise DuplicateNameException(name)
		else:
			self.categories.add(Category.Category(name, paidBy, sharedBy))
			self.emit("categories")
	
	def changeCategory(self, oldName, newName, paidBy, sharedBy):
		if oldName != newName and self.categoryExists(newName):
			raise DuplicateNameException(newName)
		elif oldName != newName:
			category = self.findCategory(oldName)
			newCategory = Category.Category(newName, paidBy, sharedBy)
			self._swapCategory(category, newCategory)
		else:
			category = self.findCategory(oldName)
			category.setPaidBy(paidBy)
			category.setSharedBy(sharedBy)
		
		self.emit("categories")
	
	def removeCategory(self, name):
		if self.categoryExists(name):
			category = self.findCategory(name)
			self._swapCategory(category, None)
			self.emit("categories")
			
	def _swapCategory(self, category, newCategory):
		self.categories.remove(category)
		if newCategory:
			self.categories.add(newCategory)
		
		[bill.swapCategory(category, newCategory) for bill in self.bills]
	
	def getCategories(self):
		return sorted(self.categories)
	
	def findCategory(self, name):
		for category in self.categories:
			if category == name or Utils.strEq(category.name, name):
				return category
		raise UnknownNameException(name)
	
	def categoryExists(self, name):
		for category in self.categories:
			if category == name or Utils.strEq(category.name, name):
				return True
		return False
		
	#############################
	#####  Bill Functions  ######
		
	def addBill(self, *args):
		bill = Bill.Bill(*args)
		self._addBill(bill)
		
	def _addBill(self, bill):
		self.bills.add(bill)
		bill.ID = self.__current_bill_ID
		self.__current_bill_ID += 1
		self.billsDict[bill.ID] = bill
		self.emit("bills")
		
	def changeBill(self, ID, value, date, paidBy, sharedBy, details, comments):
		bill = self.findBill(ID)
		unsorted = False
		if date != bill.getDate():
			unsorted = True
		
		bill.setValue(value)
		bill.setDate(date)
		bill.setPaidBy(paidBy)
		bill.setSharedBy(sharedBy)
		bill.setDetails(details)
		bill.setComments(comments)
		
		if unsorted:
			self.bills.resort()
		
		self.emit("bills")
		
	def removeBill(self, ID):
		bill = self.findBill(ID)
		if bill:
			self.bills.remove(bill)
			del self.billsDict[ID]
			self.emit("bills")
		
	def getBills(self):
		return self.bills.copy()
	
	def findBill(self, ID):
		return self.billsDict.get(ID)
	
	#############################
	###  Calculate Functions  ###
	
	def totalPersonShared(self, person, billsList=None):
		if billsList == None:
			billsList = self.getBills()
		elif not isinstance(billsList, BillsList.BillsList):
			billsList = BillsList.BillsList(billsList)
		return billsList.totalPersonShared(person)
			
	def totalPersonPaid(self, person, billsList=None):
		if billsList == None:
			billsList = self.getBills()
		elif not isinstance(billsList, BillsList.BillsList):
			billsList = BillsList.BillsList(billsList)
		return billsList.totalPersonPaid(person)
